#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
/*
    消息队列 (Message Queues - msgget, msgsnd, msgrcv): 
        内核维护的消息链表，进程可以按特定类型发送和接收结构化的消息。
    底层原理：
        内核中的链表 (或优先队列)。每个消息有类型、长度、数据。
    适用场景：
        无亲缘进程间结构化消息传递；
        需要区分消息类型或优先级；
        无需复杂同步
    优点：
        结构化数据；
        突破亲缘限制；
        支持优先级/类型；
        内核处理同步 (阻塞接收)
    缺点：
        涉及内核拷贝 (速度不如共享内存)；
        系统范围资源需管理 (ipcrm)；
        消息大小和队列深度有限制；
        POSIX 和 SysV 接口不统一。
    数据读取与时效性：
        消费型读取 (按类型或FIFO)。
        消息一旦被接收即从队列移除。
        天然保证顺序 (按类型/优先级)。
        接收端可阻塞等待特定类型新消息，保证读到的是最新的。
*/

/*  
1、创建一个key，用于非血缘关系进程通信
    key_t ftok(const char *pathname, int proj_id); 
2、创建一个消息队列
    int msgget(key_t key, int msgflg); 
3、删除、设置、获取消息队列属性                
    int msgctl(int msqid, int cmd, struct msqid_ds *buf);  
4、 消息发送，注意 msgsz 这个大小只设置mtext的大小，不包含mtype的大小
    int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); 
5、消息接收
    ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 
*/

struct msgbuf {
    long mtype;         /* message type, must be > 0 */
    char mtext[1024];    /* message data */
};

int main(int argc, char *argv){
    int ret = 0;
    int len;
    pid_t pid;
    key_t key = ftok("./msgget.txt", 'a');
    if(key < 0){
        perror("ftok() error:");
        exit(-1);
    }

    //创建消息队列
    int msgid = msgget(key, IPC_CREAT | 0777);
    if(msgid < 0){
       perror("msgget() error:");
       exit(-1);
    }
    system("ipcs -q");

    pid = fork();
    if(pid < 0){
        perror("fork() error!");
        exit(-1);
    }

    if(pid > 0){
        struct msgbuf sendMsg;
        sendMsg.mtype = 200;
        while(1){
            //printf("process B father send:");
            memset(sendMsg.mtext, 0, sizeof(sendMsg.mtext));
            fgets(sendMsg.mtext, sizeof(sendMsg.mtext), stdin);
            len = strlen(sendMsg.mtext);
            if (len > 0 && sendMsg.mtext[len - 1] == '\n') {
                sendMsg.mtext[len - 1] = '\0';  // 替换换行符
            }
            //msgflg:IPC_NOWAIT 非阻塞,0是阻塞
            ret = msgsnd(msgid, (const void *)&sendMsg, sizeof(sendMsg.mtext), 0);
            if(ret < 0){
                perror("msgsnd() error:");
                exit(-1);
            }
            if(!strncmp(sendMsg.mtext, "exit", 4)){
                break;
            }
        }
        
        //等待子进程退出
        wait(NULL);
        //删除消息队列
        ret = msgctl(msgid, IPC_RMID, NULL);
        if(ret < 0){
            perror("msgctl() error:");
            exit(-1);
        }
        system("ipcs -q");
    }else{
        struct msgbuf recvMsg;
        recvMsg.mtype = 100; //100是消息类型
        while(1){
            memset(recvMsg.mtext, 0, sizeof(recvMsg.mtext));
            ssize_t count = msgrcv(msgid, (void *)&recvMsg, sizeof(recvMsg.mtext), 100, 0);
            if(count < 0){
                perror("msgrcv() error:");
                exit(-1);
            }        
            printf("recv:msg = [%s]\n", recvMsg.mtext);
            if(!strncmp(recvMsg.mtext, "exit", 4)){
                 break;
            }
        }
    }
    return 0;
}
